package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"sync/atomic"
)

type searchResult struct {
	//文件全路径
	absoluteName [200]string
	// 被引用统计
	count uint32
	// 文件名
	name string
	//所有同名统计
	totalCount uint32
}

// 所有文件总的匹配次数统计
var matchCount uint32

//文件后缀名统计，key为后缀，value为次数，最大30种后缀名
var filePatterns = make(map[string]int, 30)

//文件被引用统计，key为文件名(绝对路径)，value为被引用次数
var fileContents = make(map[string]searchResult, 1000)

//搜索文件个数统计
var sepcFileCount = 0

// debug
var debug = false
var autoDelete = false

// 退出用channel
var contentCh = make(chan int, 512)
var suffixCh = make(chan int, 512)

// 查看查找结果
func showResult(searchPath string) {
	var totalRef = 0
	var keys []string

	for k := range fileContents {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	if debug {
		fmt.Printf("查找路径  %s\n", searchPath)
		fmt.Printf("总计查找  %d 文件\n", sepcFileCount)
		fmt.Printf("匹配次数  %d \n", matchCount)
		fmt.Printf("文件类型统计\n----------------------\n")
		for key, value := range filePatterns {
			fmt.Printf("pattern: %-10s count: %d\n", key, value)
		}

		fmt.Printf("\n\n文件被引用统计\n----------------------\n")
		for _, k := range keys {
			obj := fileContents[k]
			fmt.Printf("pattern: %-30s reference: %-3d sameName: %d\n", k, obj.count, obj.totalCount)
			totalRef++
		}
		fmt.Printf("所有被引用次数: %d\n", totalRef)
	}

	fmt.Printf("\n\n文件未被引用统计\n----------------------\n")
	totalRef = 0
	for _, k := range keys {
		var i uint32
		file := fileContents[k]
		if file.count == 0 {
			fmt.Printf("%-30s\n", file.name)
			for i = 0; i < file.totalCount; i++ {
				tempObj := file.absoluteName[i]
				totalRef++
				//是否自动删除
				if autoDelete {
					err := os.Remove(tempObj)
					errStr := "删除成功"
					if err != nil {
						errStr = "删除失败"
					}
					fmt.Printf("\t%-30s [%s]\n", tempObj, errStr)
				} else {
					fmt.Printf("\t%-30s\n", tempObj)
				}
			}
		}
	}
	fmt.Printf("所有未被引用文件个数: %d\n", totalRef)
}

// 查找符合后缀的文件
// searchType suffix表示后缀, content表示内容
func findAll(searchPath string, filePattern string) {
	// var pathArray [100]string
	var count uint32
	var fileSuffix = ""
	var hitCount = 0
	var fileName = ""

	filePattern = fmt.Sprintf(".%s", filePattern)

	filepath.Walk(searchPath, func(onePath string, info os.FileInfo, err error) error {
		if err != nil {
		}
		//find filename
		if !info.IsDir() {

			fileName = info.Name()
			fileSuffix = path.Ext(fileName)
			filePatterns[fileSuffix]++
			sepcFileCount++
			// 查找是否命中后缀
			if filePattern == fileSuffix {
				hitCount++
				if info.Size() < 1024*1024*10 {
					// 在查找路径中，查找该文件名在文件被引用的次数
					// fmt.Printf("查找 %s 被引用次数\n", fileName)
					temp := fileContents[fileName]
					temp.name = fileName
					count = temp.totalCount
					temp.absoluteName[count] = onePath
					temp.totalCount++
					fileContents[fileName] = temp
					// fileContents[fileName].name = fileName
					// count = fileContents[fileName].totalCount
					// fileContents[fileName].absoluteName[count] = onePath
					// fileContents[fileName].totalCount++

					go findFile(searchPath, fileName)
					//等待该次路径查找完成
					<-suffixCh
				}
			}

		}
		return nil
	})

}

// 在指定的搜索路径中找到指定的字符串
func findFile(path string, text string) {
	var pathArray [100]string
	var count = 0
	var fileCount = 0
	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
		}
		//find text
		if !info.IsDir() {
			fileCount++
			// fmt.Printf("%200s %d bytes\n", info.Name(), info.Size())
			if info.Size() < 1024*1024*10 {
				pathArray[count] = path
				count++
				//每一百个执行任务
				if count >= 100 {
					count = 0
					go findText(pathArray[0:100], text)
					//等待content任务完成
					<-contentCh
				}
			}
		}
		return nil
	})
	//确保所有文件处理完成
	go findText(pathArray[0:count], text)
	<-contentCh
	// 表明该此后缀任务查找已经完成
	suffixCh <- 1
}

// 在指定文件集合中，查找指定内容是否出现
func findText(paths []string, text string) {
	for _, path := range paths {
		fi, err := os.Open(path)
		if err != nil {
			panic(err)
		}
		defer fi.Close()
		fd, err := ioutil.ReadAll(fi)
		if err != nil {
			panic(err)
		}

		if strings.Index(string(fd), text) > -1 {
			// fmt.Printf("查找文件 %s 在 %s 被引用\n", text, path)
			temp := fileContents[text]
			temp.count++
			fileContents[text] = temp
			atomic.AddUint32(&matchCount, 1)
			if debug {
				fmt.Println(path)
			}
		}
	}
	contentCh <- 1
}

func main() {
	args := os.Args
	if len(args) < 4 {
		fmt.Println("请输入 搜索路径 文件类型 debug[0|1]")
		return
	}
	path := args[1]
	fileType := args[2]

	if args[3] == "1" {
		debug = true
	}
	if len(args) == 5 {
		if args[4] == "1" {
			autoDelete = true
		}
	}

	_, err := os.Stat(path)

	if err != nil {
		fmt.Println("该路径不存在, 退出")
	} else {
		findAll(path, fileType)
		showResult(path)
	}
}
